package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.service.SampleServiceAsync;
import org.oslims.client.service.SampleTypeServiceAsync;
import org.oslims.client.uicomponents.SelectionColumn;
import org.oslims.client.uicomponents.SortableHeader;
import org.oslims.client.uicomponents.StyleableFlexTable;
import org.oslims.client.uicomponents.TagCheckBox;
import org.oslims.client.uicomponents.editbox.EditMixtureBox;
import org.oslims.client.uicomponents.editbox.SearchSamplesBox;
import org.oslims.client.view.impl.MixtureEditorViewImpl;
import org.oslims.domain.AbstractSample;
import org.oslims.domain.Mixture;
import org.oslims.domain.MixtureComponent;
import org.oslims.domain.Request;
import org.oslims.domain.Sample;
import org.oslims.domain.SampleType;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.validation.client.InvalidConstraint;
import com.google.gwt.validation.client.interfaces.IValidator;
import com.google.gwt.view.client.ListView;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.ListView.Delegate;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = MixtureEditorViewImpl.class)
public class MixturePresenter extends BasePresenter<MixtureEditorViewImpl, MainEventBus> {

	private IValidator<Mixture> mixtureValidator;
	private List<SampleType> sampleTypeList;

	private Request currentRequest;

	private SampleServiceAsync sampleService;

	@InjectService
	public void setSampleService(SampleServiceAsync sampleService) {
		this.sampleService = sampleService;
	}

	private SampleTypeServiceAsync sampleTypeService;

	private String currentSortProperty = "code";
	private Boolean currentSortDesc = false;

	public MixturePresenter() {
		mixtureValidator = GWT.create(Mixture.class);
	}

	public void onInizializeMixtureManagement(TabPanel tp) {
		tp.add(view.getViewWidget(), "Mixtures");
	}

	public void onRefreshMixtureView(Request currentRequest) {
		this.currentRequest = currentRequest;
		refreshChoices();
		refreshData();
	}

	public void bind() {

		bindUI();

		addColumns();

		setDelegate();

	}

	private void setDelegate() {
		view.getTable().setDelegate(new Delegate<Mixture>() {

			@Override
			public void onRangeChanged(ListView<Mixture> listView) {

				final Range r = listView.getRange();

				sampleService.getAllMixturesByRequest(currentRequest, r.getStart(), r.getLength(), currentSortProperty, currentSortDesc, new AsyncCall<List<Mixture>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<Mixture> result) {
						view.getTable().setData(r.getStart(), r.getLength(), result);
						view.getTable().redraw();
					}
				});
			}
		});
	}

	private void addColumns() {

		SelectionColumn<Mixture> selColumn = new SelectionColumn<Mixture>(view.getSelectionModel());
		view.getTable().addColumn(selColumn);

		final SortableHeader codeHeader = new SortableHeader("Code", "code");
		codeHeader.setSorted(true);

		codeHeader.setUpdater(new ValueUpdater<String>() {

			@Override
			public void update(String value) {
				codeHeader.toggleReverseSort();
				currentSortProperty = codeHeader.getPropertyName();
				currentSortDesc = codeHeader.getReverseSort();
				view.getTable().refresh();
				view.getTable().refreshHeaders();
			}
		});

		view.getTable().addColumn(new TextColumn<Mixture>() {

			@Override
			public String getValue(Mixture object) {
				return object.getCode();
			}
		}, codeHeader);

		view.getTable().addColumn(new TextColumn<Mixture>() {

			@Override
			public String getValue(Mixture object) {
				return object.getSampleType().toString();
			}
		}, "Sample Type");

		view.getTable().addColumn(new TextColumn<Mixture>() {

			@Override
			public String getValue(Mixture object) {
				return object.getOrigin();
			}
		}, "Origin");

		view.getTable().addColumn(new TextColumn<Mixture>() {

			@Override
			public String getValue(Mixture object) {
				return object.getDescription();
			}
		}, "Description");

		view.getTable().addColumn(new TextColumn<Mixture>() {

			@SuppressWarnings("deprecation")
			@Override
			public String getValue(Mixture object) {
				return object.getSamplingTime().toLocaleString();
			}
		}, "Sampling Date/Time");

		view.getTable().addColumn(new TextColumn<Mixture>() {

			@Override
			public String getValue(Mixture object) {
				return object.getStoragePlace();
			}
		}, "Storage Place");

		view.getTable().addColumn(new TextColumn<Mixture>() {

			@Override
			public String getValue(Mixture object) {
				return object.getState();
			}
		}, "State");

		Column<Mixture, String> editColumn = new Column<Mixture, String>(new ButtonCell()) {

			@Override
			public String getValue(Mixture object) {
				return "Edit";
			}
		};

		editColumn.setFieldUpdater(new FieldUpdater<Mixture, String>() {

			@Override
			public void update(int index, Mixture object, String value) {

				int selectedIndex = -1;
				List<String> sampleTypes = new ArrayList<String>();

				for (SampleType st : sampleTypeList) {
					sampleTypes.add(st.toString());
					if (st.toString().equals(object.getSampleType().toString())) {
						selectedIndex = sampleTypes.size() - 1;
					}
				}

				view.getEditMixtureBox().getSampleType().setSelectionItems(sampleTypes);
				view.getEditMixtureBox().getSampleType().getInputBox().setSelectedIndex(selectedIndex);

				view.getEditMixtureBox().showEdit(object);
			}
		});

		view.getTable().addColumn(editColumn);
	}

	private void refreshData() {
		sampleService.getMixtureCount(currentRequest, new AsyncCall<Integer>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(Integer result) {
				view.getTable().setDataSize(result, true);

				if (result < view.getDefaultPageSize()) {
					view.getPager().setVisible(false);
					view.getTable().setPageSize(result);
				}
				view.getTable().refresh();
			}
		});
	}

	private void bindUI() {
		view.getAddMixtureButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				List<String> sampleTypes = new ArrayList<String>();

				for (SampleType st : sampleTypeList) {
					sampleTypes.add(st.toString());
				}

				view.getEditMixtureBox().getSampleType().setSelectionItems(sampleTypes);
				view.getEditMixtureBox().showNew();
			}
		});

		view.getCopyMixtureButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				copySelectedMixture();
			}
		});

		view.getMarkMixtureAsPreparedButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				int i = view.getSelectionModel().getSelectedSet().size();

				if (i == 0) {
					eventBus.error("No Mixture selected!");
					return;
				}

				view.getEnterStoragePlaceBox().show();
			}
		});

		view.getEnterStoragePlaceBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				String storagePlace = view.getEnterStoragePlaceBox().getStoragePlaceField().getValue();

				if (storagePlace.length() > 0) {
					markSelectedPrepared(view.getEnterStoragePlaceBox().getStoragePlaceField().getValue());
					view.getEnterStoragePlaceBox().hide();
				} else {
					eventBus.error("Storage Place cannot be empty");
				}

			}
		});

		view.getEnterStoragePlaceBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				view.getEnterStoragePlaceBox().hide();
			}

		});

		view.getEditMixtureBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditMixtureBox().hide();
			}
		});

		view.getEditMixtureBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				if (view.confirmEdit("Do you want to save your changes?"))
					saveMixture();
			}
		});

		final SearchSamplesBox searchMixtureComponentBox = view.getEditMixtureBox().getSearchSamplesBox();

		searchMixtureComponentBox.getSearchButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				searchSamplesForMixtureComponents();
			}
		});

		searchMixtureComponentBox.getOkButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				importSamplesForMixtureComponents();
			}
		});

		searchMixtureComponentBox.getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				searchMixtureComponentBox.hide();
			}
		});

		view.getEditMixtureBox().getAddComponentButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditMixtureBox().getSearchSamplesBox().show();
			}
		});

		view.getEditMixtureBox().getRemoveComponentButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {

				view.getEditMixtureBox().removeSelectedComponents();
			}
		});

	}

	protected void importSamplesForMixtureComponents() {
		final StyleableFlexTable searchTable = view.getEditMixtureBox().getSearchSamplesBox().getSearchTable();
		final CellTable<MixtureComponent> mixtureComponentTable = view.getEditMixtureBox().getCompositionTable();

		List<Sample> importList = new ArrayList<Sample>();
		List<MixtureComponent> currentList = mixtureComponentTable.getDisplayedItems();

		for (int i = 2; i < searchTable.getRowCount(); i++) {
			TagCheckBox cbx = (TagCheckBox) searchTable.getWidget(i, 0);

			if (cbx.getValue())
				importList.add((Sample) cbx.getTag());
		}

		if (importList.size() > 0) {

			Double sum = Double.parseDouble(view.getEditMixtureBox().getSumField().getText());

			for (Sample s : importList) {
				MixtureComponent mc = new MixtureComponent();
				mc.setSample(s);
				mc.setAmmount(25d);

				Boolean contains = false;
				for (MixtureComponent mc2 : currentList) {
					if (mc2.getSample().equals(mc.getSample())) {
						contains = true;
					}
				}
				if (!contains) {
					sum += 25d;
					currentList.add(mc);
				}
			}

			mixtureComponentTable.setDataSize(currentList.size(), true);
			mixtureComponentTable.setData(0, currentList.size(), currentList);
			mixtureComponentTable.redraw();
			view.getEditMixtureBox().getSearchSamplesBox().hide();
			view.getEditMixtureBox().getSumField().setText(sum.toString());
		}

	}

	protected void searchSamplesForMixtureComponents() {

		SearchSamplesBox searchBox = view.getEditMixtureBox().getSearchSamplesBox();
		final StyleableFlexTable searchTable = view.getEditMixtureBox().getSearchSamplesBox().getSearchTable();

		String sampleCode = searchBox.getCodeBox().getValue();
		String description = searchBox.getDescriptionBox().getValue();
		String origin = searchBox.getOriginBox().getValue();
		String request = searchBox.getRequestBox().getValue();
		String sampleType = searchBox.getSampleTypeBox().getValue();
		String storagePlace = searchBox.getStoragePlaceBox().getValue();

		if (sampleCode == null)
			sampleCode = "";
		if (description == null)
			description = "";
		if (origin == null)
			origin = "";
		if (request == null)
			request = "";
		if (sampleType == null)
			sampleType = "";
		if (storagePlace == null)
			storagePlace = "";

		sampleService.searchAllSamples(sampleCode, description, origin, request, sampleType, storagePlace, new AsyncCall<List<AbstractSample>>() {

			@Override
			protected void onSuccessE(List<AbstractSample> result) {

				while (searchTable.getRowCount() > 2)
					searchTable.removeRow(2);

				for (int i = 0; i < result.size(); i++) {
					searchTable.setWidget(i + 2, 0, new TagCheckBox(result.get(i)));
					searchTable.setText(i + 2, 1, result.get(i).getCode());
					searchTable.setText(i + 2, 2, result.get(i).getDescription());
					searchTable.setText(i + 2, 3, result.get(i).getOrigin());
					String requestList = "";

					for (Request r : result.get(i).getRequests()) {
						requestList += r.getCode() + ", ";
					}
					searchTable.setText(i + 2, 4, requestList.substring(0, requestList.length() - 2));
					searchTable.setText(i + 2, 5, result.get(i).getSampleType().getName());
					searchTable.setText(i + 2, 6, result.get(i).getStoragePlace());
				}
			}

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});

	}

	protected void markSelectedPrepared(String storagePlace) {

		List<Mixture> selectedMixtures = new ArrayList<Mixture>();

		for (Mixture m : view.getSelectionModel().getSelectedSet()) {
			selectedMixtures.add(m);
		}

		sampleService.markMixturesAsPrepared(selectedMixtures, storagePlace, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {
				refreshData();
			}

			@Override
			public void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});

	}

	protected void copySelectedMixture() {

		int i = view.getSelectionModel().getSelectedSet().size();

		if (i > 1) {
			eventBus.error("Only one mixture can be copied at a time");
			return;
		}

		if (i == 0) {
			eventBus.error("No Mixture selected!");
			return;
		}

		Mixture newMixture = new Mixture();
		Mixture existingMixture = null;

		for (Mixture m : view.getSelectionModel().getSelectedSet()) {
			existingMixture = m;
		}

		newMixture.setDescription(existingMixture.getDescription());
		newMixture.setOrigin(existingMixture.getOrigin());
		newMixture.getRequests().add(currentRequest);
		newMixture.setSampleType(existingMixture.getSampleType());
		newMixture.setSamplingTime(existingMixture.getSamplingTime());
		newMixture.setState(existingMixture.getState());
		newMixture.setStoragePlace(existingMixture.getStoragePlace());

		for (MixtureComponent mc : existingMixture.getComponents()) {
			MixtureComponent mcNew = new MixtureComponent();
			mcNew.setAmmount(mc.getAmmount());
			mcNew.setRootSample(mc.getRootSample());
			mcNew.setSample(mc.getSample());
			newMixture.getComponents().add(mcNew);
		}

		sampleService.saveOrUpdateMixture(newMixture, new AsyncCall<Void>() {

			@Override
			protected void onSuccessE(Void result) {
				refreshData();
			}

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});

	}

	private void saveMixture() {

		EditMixtureBox editBox = view.getEditMixtureBox();
		Mixture tempMixture = editBox.getMixture();

		if (editBox.getMixture() == null) {
			tempMixture = new Mixture();
			tempMixture.setState("Not Prepared");
		}

		tempMixture.setOrigin(editBox.getOrigin().getValue());
		tempMixture.setDescription(editBox.getDescription().getHTML());
		tempMixture.setSamplingTime(editBox.getSamplingTime().getValue());

		if (!tempMixture.getRequests().contains(currentRequest)) {
			tempMixture.getRequests().add(currentRequest);
		}

		String selectedSampleType = editBox.getSampleType().getInputBox().getItemText(editBox.getSampleType().getInputBox().getSelectedIndex());

		for (SampleType st : sampleTypeList) {
			if (st.toString().equals(selectedSampleType)) {
				tempMixture.setSampleType(st);
				break;
			}
		}

		tempMixture.getComponents().clear();

		for (MixtureComponent mc : view.getEditMixtureBox().getCompositionTable().getDisplayedItems()) {
			tempMixture.getComponents().add(mc);
		}

		if (tempMixture.getComponents().size() > 0 && Double.parseDouble(view.getEditMixtureBox().getSumField().getText()) != 100d) {
			eventBus.error("Sum of Components must be 100!");
			return;
		}

		Set<InvalidConstraint<Mixture>> mixtureErrors = mixtureValidator.validate(tempMixture);

		if (mixtureErrors.size() > 0) {
			for (InvalidConstraint<Mixture> ic : mixtureErrors) {
				editBox.showError(ic.getItemName(), ic.getMessage());
			}
			return;
		}

		sampleService.saveOrUpdateMixture(tempMixture, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {
				view.getEditMixtureBox().hide();
				refreshData();
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});
	}

	private void refreshChoices() {

		sampleTypeService.getAllSampleType(new AsyncCall<List<SampleType>>() {

			@Override
			protected void onSuccessE(List<SampleType> result) {
				sampleTypeList = result;
			}

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});
	}

	@InjectService
	public void setSampleTypeService(SampleTypeServiceAsync sampleTypeService) {
		this.sampleTypeService = sampleTypeService;
	}
}
